// @Date         2021-11-12
// @Author       Aaron Zhang
// @Description
// @FileName      cron.go

package command

import (
	"fmt"
	"github.com/erikdubbelboer/gspt"
	"github.com/scylladb/termtables"
	"github.com/sevlyar/go-daemon"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"syscall"
	"time"
	"zin/framework/cobra"
	"zin/framework/util"
)

var cronDaemon bool

func initCronCommand() *cobra.Command {
	parseCronFlags()
	cronCommand.AddCommand(cronStartCommand, cronStopCommand, stateCommand, restartCommand, listCommand)
	return cronCommand
}

var cronCommand = &cobra.Command{
	Use:   "cron",
	Short: "cron控制命令",
	RunE: func(cmd *cobra.Command, args []string) error {
		// 打印帮助文档
		cmd.Help()
		return nil
	},
}

var cronStartCommand = &cobra.Command{
	Use:   "start",
	Short: "启动一个Cron脚本",
	RunE: func(cmd *cobra.Command, args []string) error {
		appService := util.GetAppProvider()

		pidFile := path.Join(appService.RuntimeFolder(), "cron.pid")
		logFile := path.Join(appService.LogFolder(), "cron.log")
		baseFolder := appService.BaseFolder()

		if cronDaemon {

			// 创建一个Context
			cntxt := &daemon.Context{
				// 设置pid文件
				PidFileName: pidFile,
				PidFilePerm: 0664,
				// 设置日志文件
				LogFileName: logFile,
				LogFilePerm: 0640,
				// 设置工作路径
				WorkDir: baseFolder,
				// 设置所有设置文件的mask，默认为750
				Umask: 027,
				// 子进程的参数，按照这个参数设置，子进程的命令为 ./hade cron start --daemon=true
				Args: []string{"", "cron", "start", "--daemon=true"},
			}

			// 启动子进程，d不为空表示当前是父进程，d为空表示当前是子进程
			d, err := cntxt.Reborn()
			if err != nil {
				return err
			}

			if d != nil {
				// 父进程直接打印启动成功信息，不做任何操作
				fmt.Println("cron serve started with daemon, pid:", d.Pid)
				fmt.Println("log file:", logFile)
				return nil
			}

			// 子进程执行Cron.Run
			defer cntxt.Release()
			fmt.Println("daemon started")

			gspt.SetProcTitle("zin cron")
			cmd.Root().Cron.Run()

			return nil
		}

		// not deamon mode
		content := strconv.Itoa(os.Getpid())
		fmt.Println("cron serve started, pid:", content)

		err := ioutil.WriteFile(pidFile, []byte(content), 0664)
		if err != nil {
			return err
		}

		gspt.SetProcTitle("zin cron")
		cmd.Root().Cron.Run()

		return nil
	},
}

var cronStopCommand = &cobra.Command{
	Use:   "stop",
	Short: "停止cron定时任务",
	RunE: func(cmd *cobra.Command, args []string) error {
		appService := util.GetAppProvider()
		pidFile := filepath.Join(appService.RuntimeFolder(), "cron.pid")
		content, err := ioutil.ReadFile(pidFile)
		if err != nil {
			return err
		}

		if len(content) > 0 {
			pidInt, _ := strconv.Atoi(string(content))

			err := syscall.Kill(pidInt, syscall.SIGTERM)
			if err != nil {
				return err
			}

			err = os.Remove(pidFile)
			if err != nil {
				return err
			}
		}
		fmt.Println("cron serve stoped")
		return nil
	},
}

var stateCommand = &cobra.Command{
	Use:   "state",
	Short: "查看cron进程状态",
	RunE: func(cmd *cobra.Command, args []string) error {
		appService := util.GetAppProvider()

		// GetPid
		serverPidFile := filepath.Join(appService.RuntimeFolder(), "cron.pid")

		content, err := ioutil.ReadFile(serverPidFile)
		if err != nil {
			return err
		}

		if content != nil && len(content) > 0 {
			pid, err := strconv.Atoi(string(content))
			if err != nil {
				return err
			}

			if util.CheckProcessExist(pid) {
				fmt.Println("cron server started, pid:", pid)
				return nil
			}
		}
		fmt.Println("no cron server start")

		return nil
	},
}

var restartCommand = &cobra.Command{
	Use:   "restart",
	Short: "restart cron service",
	RunE: func(cmd *cobra.Command, args []string) error {
		appService := util.GetAppProvider()

		// GetPid
		serverPidFile := filepath.Join(appService.RuntimeFolder(), "cron.pid")
		content, err := ioutil.ReadFile(serverPidFile)
		if err != nil {
			return err
		}

		if len(content) > 0 {
			pid, err := ioutil.ReadFile(serverPidFile)
			if err != nil {
				return err
			}

			pidInt, _ := strconv.Atoi(string(pid))
			if util.CheckProcessExist(pidInt) {
				if err := syscall.Kill(pidInt, syscall.SIGTERM); err != nil {
					return err
				}
				fmt.Println("restarting...")

				time.Sleep(time.Second * 1)
			}
		}
		cronDaemon = true
		return cronStartCommand.RunE(cmd, args)
	},
}

var listCommand = &cobra.Command{
	Use:   "list",
	Short: "列出所有定时任务",
	RunE: func(cmd *cobra.Command, args []string) error {
		specs := cmd.Root().CronSpecs

		t := termtables.CreateTable()
		t.AddHeaders("类型", "名称", "指令", "用途", "介绍", "服务名")

		for _, cronSpec := range specs {
			if cronSpec.Cmd != nil {
				t.AddRow(cronSpec.Type, cronSpec.Title, cronSpec.Spec, cronSpec.Cmd.Use, cronSpec.Cmd.Short, cronSpec.ServiceName)
			} else {
				t.AddRow(cronSpec.Type, cronSpec.Title, cronSpec.Spec, "callback func", "--", cronSpec.ServiceName)
			}
		}

		res := t.Render()
		fmt.Println(res)

		return nil
	},
}

//解析 start 后面的 flag参数，控制是否后台运行
func parseCronFlags() {
	cronStartCommand.Flags().BoolVarP(&cronDaemon, "demon", "d", false, "是否后台运行")
}
